home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 16 / CU Amiga Magazine's Super CD-ROM 16 (1997-10-16)(EMAP Images)(GB)[!][issue 1997-11].iso / CUCD / Graphics / Ghostscript / source / gdevbgi.c < prev    next >
C/C++ Source or Header  |  1996-09-17  |  19KB  |  662 lines

  1. /* Copyright (C) 1989, 1990, 1991, 1993, 1994, 1996 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19. /* gdevbgi.c */
  20. /* Driver for Borland Graphics Interface (BGI) */
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include <conio.h>
  24. #include <graphics.h>
  25. #include <mem.h>
  26. #include "gserrors.h"
  27. #include "gx.h"
  28. #include "gxdevice.h"
  29. #include "gxxfont.h"
  30.  
  31. #ifndef BGI_LIB                /* may be set in makefile */
  32. #  define BGI_LIB ""
  33. #endif
  34.  
  35. /*
  36.  * BGI supports these video cards:
  37.  *   Hercules, CGA, MCGA, EGA, VGA, AT&T 400, IBM 8514, PC3270.
  38.  * Highest resolution mode is used with all these video cards.
  39.  * EGA and VGA display 16 colors, the rest are black-and-white only.
  40.  * In addition, the environment variable BGIUSER may be used
  41.  * to define a user-supplied Super VGA driver: see the use.doc file
  42.  * for details.
  43.  */
  44. #define SUPER_VGA 999            /* bogus # for user-defined driver */
  45.  
  46. /* See gxdevice.h for the definitions of the procedures. */
  47.  
  48. dev_proc_open_device(bgi_open);
  49. dev_proc_close_device(bgi_close);
  50. dev_proc_map_rgb_color(bgi_map_rgb_color);
  51. dev_proc_map_color_rgb(bgi_map_color_rgb);
  52. dev_proc_fill_rectangle(bgi_fill_rectangle);
  53. dev_proc_tile_rectangle(bgi_tile_rectangle);
  54. dev_proc_copy_mono(bgi_copy_mono);
  55. dev_proc_copy_color(bgi_copy_color);
  56. dev_proc_draw_line(bgi_draw_line);
  57. dev_proc_get_xfont_procs(bgi_get_xfont_procs);
  58.  
  59. /* The device descriptor */
  60. typedef struct gx_device_bgi_s gx_device_bgi;
  61. struct gx_device_bgi_s {
  62.     gx_device_common;
  63.     int display_mode;
  64.     struct text_info text_mode;
  65. };
  66. #define bgi_dev ((gx_device_bgi *)dev)
  67. private gx_device_procs bgi_procs = {
  68.     bgi_open,
  69.     NULL,            /* get_initial_matrix */
  70.     NULL,            /* sync_output */
  71.     NULL,            /* output_page */
  72.     bgi_close,
  73.     bgi_map_rgb_color,
  74.     bgi_map_color_rgb,
  75.     bgi_fill_rectangle,
  76.     bgi_tile_rectangle,
  77.     bgi_copy_mono,
  78.     bgi_copy_color,
  79.     bgi_draw_line,
  80.     NULL,            /* get_bits */
  81.     NULL,            /* get_props */
  82.     NULL,            /* put_props */
  83.     NULL,
  84.     bgi_get_xfont_procs
  85. };
  86. gx_device_bgi far_data gs_bgi_device = {
  87.     std_device_std_body(gx_device_bgi, &bgi_procs, "bgi",
  88.       0, 0,        /* width and height are set in bgi_open */
  89.       1, 1        /* density is set in bgi_open */
  90.     )
  91. };
  92.  
  93. /* Detection procedure for user-defined driver. */
  94. private int huge
  95. detectVGA(void)
  96. {    return gs_bgi_device.display_mode;
  97. }
  98.  
  99. /* Open the BGI driver for graphics mode */
  100. int
  101. bgi_open(gx_device *dev)
  102. {    int driver, mode;
  103.     char *bgi_user = getenv("BGIUSER");
  104.     char *bgi_path = getenv("BGIPATH");
  105.  
  106.     gettextinfo(&bgi_dev->text_mode);
  107.  
  108.     if ( bgi_path == NULL )
  109.         bgi_path = BGI_LIB;
  110.     if ( bgi_user != NULL )
  111.        {    /* A user-supplied driver is specified as "mode.dname", */
  112.         /* where mode is a hex number and dname is the name */
  113.         /* of the driver file. */
  114.         char dname[40];
  115.         if ( strlen(bgi_user) > sizeof(dname) ||
  116.              sscanf(bgi_user, "%x.%s", &mode, dname) != 2
  117.            )
  118.            {    eprintf("BGIUSER not in form nn.dname.\n");
  119.             exit(1);
  120.            }
  121.         gs_bgi_device.display_mode = mode;    /* sigh.... */
  122.         installuserdriver(dname, detectVGA);
  123.         driver = DETECT;
  124.         initgraph(&driver, &mode, bgi_path);
  125.         driver = SUPER_VGA;
  126.        }
  127.     else                /* not user-defined driver */
  128.        {    /* We include the CGA driver in the executable, */
  129.         /* so end-users don't have to have the BGI files. */
  130.         if ( registerfarbgidriver(CGA_driver_far) < 0 )
  131.            {    eprintf("BGI: Can't register CGA driver!\n");
  132.             exit(1);
  133.            }
  134.  
  135.         detectgraph(&driver, &mode);
  136.         if ( driver < 0 )
  137.            {    eprintf("BGI: No graphics hardware detected!\n");
  138.             exit(1);
  139.            }
  140.  
  141.         if ( driver == EGA64 )
  142.            {    /* Select 16 color video mode if video card is EGA with 64 Kb of memory */
  143.             mode = EGA64LO;
  144.            }
  145.  
  146.         /* Initialize graphics mode. */
  147.  
  148.         /* Following patch for AT&T 6300 is courtesy of */
  149.         /* Allan Wax, Xerox Corp. */
  150.         if ( driver == CGA )
  151.            {    /* The actual hardware might be an AT&T 6300. */
  152.             /* Try initializing it that way. */
  153.             int save_mode = mode;
  154.             driver = ATT400, mode = ATT400HI;
  155.             initgraph(&driver, &mode, bgi_path);
  156.             if ( graphresult() != grOk )
  157.                {    /* Nope, it was a real CGA. */
  158.                 closegraph();
  159.                 driver = CGA, mode = save_mode;
  160.                 initgraph(&driver, &mode, bgi_path);
  161.                }
  162.            }
  163.         else
  164.             initgraph(&driver, &mode, bgi_path);
  165.        }
  166.  
  167.        {    int code = graphresult();
  168.         if ( code != grOk )
  169.            {    eprintf1("Error initializing BGI driver: %s\n",
  170.                  grapherrormsg(code));
  171.             exit(1);
  172.            }
  173.        }
  174.  
  175.     setactivepage(1);
  176.     setvisualpage(1);
  177.     /* Set parameters that were unknown before opening device */
  178.  
  179.     /* Size and nominal density of screen. */
  180.     /* The following algorithm maps an appropriate fraction of */
  181.     /* the display screen to an 8.5" x 11" coordinate space. */
  182.     /* This may or may not be what is desired! */
  183.     if ( dev->width == 0 )
  184.         dev->width = getmaxx() + 1;
  185.     if ( dev->height == 0 )
  186.         dev->height = getmaxy() + 1;
  187.     if ( dev->y_pixels_per_inch == 1 )
  188.        {    /* Get the aspect ratio from the driver. */
  189.         int arx, ary;
  190.         getaspectratio(&arx, &ary);
  191.         dev->y_pixels_per_inch = dev->height / 11.0;
  192.         dev->x_pixels_per_inch =
  193.             dev->y_pixels_per_inch * ((float)ary / arx);
  194.        }
  195.  
  196.     /* Find out if the device supports color */
  197.     /* (default initialization is monochrome). */
  198.     /* We only recognize 16-color devices right now. */
  199.     if ( getmaxcolor() > 1 )
  200.        {    static gx_device_color_info bgi_16color = dci_color(4, 2, 3);
  201.         dev->color_info = bgi_16color;
  202.        }
  203.     return 0;
  204. }
  205.  
  206. /* Close the BGI driver */
  207. int
  208. bgi_close(gx_device *dev)
  209. {    closegraph();
  210.     textmode(bgi_dev->text_mode.currmode);
  211.     return 0;
  212. }
  213.  
  214. /* Map a r-g-b color to the 16 colors available with an EGA/VGA video card. */
  215. gx_color_index
  216. bgi_map_rgb_color(gx_device *dev, gx_color_value r, gx_color_value g,
  217.   gx_color_value b)
  218. {    return (gx_color_index)
  219.         ((r > gx_max_color_value / 4 ? 4 : 0) +
  220.          (g > gx_max_color_value / 4 ? 2 : 0) +
  221.          (b > gx_max_color_value / 4 ? 1 : 0) +
  222.          (r > gx_max_color_value / 4 * 3 ||
  223.           g > gx_max_color_value / 4 * 3 ? 8 : 0));
  224. }
  225.  
  226. /* Map a color code to r-g-b.  Surprisingly enough, this is algorithmic. */
  227. int
  228. bgi_map_color_rgb(gx_device *dev, gx_color_index color,
  229.   gx_color_value prgb[3])
  230. {
  231. #define icolor (int)color
  232.     gx_color_value one =
  233.         (icolor & 8 ? gx_max_color_value : gx_max_color_value / 3);
  234.     prgb[0] = (icolor & 4 ? one : 0);
  235.     prgb[1] = (icolor & 2 ? one : 0);
  236.     prgb[2] = (icolor & 1 ? one : 0);
  237.     return 0;
  238. #undef icolor
  239. }
  240.  
  241. /* Copy a monochrome bitmap.  The colors are given explicitly. */
  242. /* Color = gx_no_color_index means transparent (no effect on the image). */
  243. int
  244. bgi_copy_mono(gx_device *dev,
  245.   const byte *base, int sourcex, int raster, gx_bitmap_id id,
  246.   int x, int y, int w, int h,
  247.   gx_color_index zero, gx_color_index one)
  248. {    const byte *ptr_line = base + (sourcex >> 3);
  249.     int left_bit = 0x80 >> (sourcex & 7);
  250.     int dest_y = y, end_x = x + w;
  251.     int invert = 0;
  252.     int color;
  253.  
  254.     if ( zero == gx_no_color_index )
  255.        {    if ( one == gx_no_color_index ) return 0;
  256.         color = (int)one;
  257.        }
  258.     else
  259.        {    if ( one == gx_no_color_index )
  260.            {    color = (int)zero;
  261.             invert = -1;
  262.            }
  263.         else
  264.            {    /* Pre-clear the rectangle to zero */
  265.             setfillstyle(SOLID_FILL, (int)zero);
  266.             bar(x, y, x + w - 1, y + h - 1);
  267.             color = (int)one;
  268.            }
  269.        }
  270.  
  271.     while ( h-- )              /* for each line */
  272.        {    const byte *ptr_source = ptr_line;
  273.         register int dest_x = x;
  274.         register int bit = left_bit;
  275.         while ( dest_x < end_x )     /* for each bit in the line */
  276.            {    if ( (*ptr_source ^ invert) & bit )
  277.                 putpixel(dest_x, dest_y, color);
  278.             dest_x++;
  279.             if ( (bit >>= 1) == 0 )
  280.                 bit = 0x80, ptr_source++;
  281.            }
  282.         dest_y++;
  283.         ptr_line += raster;
  284.        }
  285.     return 0;
  286. }
  287.  
  288.  
  289. /* Copy a color pixel map.  This is just like a bitmap, except that */
  290. /* each pixel takes 4 bits instead of 1 when device driver has color. */
  291. int
  292. bgi_copy_color(gx_device *dev,
  293.   const byte *base, int sourcex, int raster, gx_bitmap_id id,
  294.   int x, int y, int w, int h)
  295. {    if ( gx_device_has_color(dev) )
  296.        {    /* color device, four bits per pixel */
  297.         const byte *line = base + (sourcex >> 1);
  298.         int dest_y = y, end_x = x + w;
  299.  
  300.         if ( w <= 0 ) return 0;
  301.         while ( h-- )              /* for each line */
  302.            {    const byte *source = line;
  303.             register int dest_x = x;
  304.             if ( sourcex & 1 )    /* odd nibble first */
  305.                {    int color =  *source++ & 0xf;
  306.                 putpixel(dest_x, dest_y, color);
  307.                 dest_x++;
  308.                }
  309.             /* Now do full bytes */
  310.             while ( dest_x < end_x )
  311.                {    int color = *source >> 4;
  312.                 putpixel(dest_x, dest_y, color);
  313.                 dest_x++;
  314.                 if ( dest_x < end_x )
  315.                    {    color =  *source++ & 0xf;
  316.                     putpixel(dest_x, dest_y, color);
  317.                     dest_x++;
  318.                    }
  319.                }
  320.             dest_y++;
  321.             line += raster;
  322.            }
  323.        }
  324.     else /* monochrome device: one bit per pixel */
  325.        {    /* bitmap is the same as bgi_copy_mono: one bit per pixel */
  326.         bgi_copy_mono(dev, base, sourcex, raster, id, x, y, w, h,
  327.             (gx_color_index)BLACK, (gx_color_index)WHITE);
  328.        }
  329.     return 0;
  330. }
  331.  
  332.  
  333. /* Fill a rectangle. */
  334. int
  335. bgi_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
  336.   gx_color_index color)
  337. {    setfillstyle(SOLID_FILL, (int)color);
  338.     bar(x, y, x + w - 1, y + h - 1);
  339.     return 0;
  340. }
  341.  
  342.  
  343. /* Tile a rectangle.  If neither color is transparent, */
  344. /* pre-clear the rectangle to color0 and just tile with color1. */
  345. /* This is faster because of how bgi_copy_mono is implemented. */
  346. /* Note that this also does the right thing for colored tiles. */
  347. int
  348. bgi_tile_rectangle(gx_device *dev, const gx_tile_bitmap *tile,
  349.   int x, int y, int w, int h, gx_color_index czero, gx_color_index cone,
  350.   int px, int py)
  351. {    int tw = tile->size.x, th = tile->size.y;
  352.     int rw, rh, tx, ty;
  353.     int code;
  354.     byte image[4+4+256];
  355.     if ( w >> 1 < tw || h >> 1 < th ||
  356.          czero == gx_no_color_index || cone == gx_no_color_index ||
  357.          imagesize(x, y, x + tw - 1, y + th - 1) > sizeof(image)
  358.        )
  359.     {    if ( czero != gx_no_color_index && cone != gx_no_color_index )
  360.         {    bgi_fill_rectangle(dev, x, y, w, h, czero);
  361.             czero = gx_no_color_index;
  362.         }
  363.         return gx_default_tile_rectangle(dev, tile, x, y, w, h, czero, cone, px, py);
  364.     }
  365.     /* Handle edge strips.  We know w and h are both large. */
  366.     rh = h % th;
  367.     if ( rh )
  368.     {    code = gx_default_tile_rectangle(dev, tile, x, y + h - rh, w, rh, czero, cone, px, py);
  369.         if ( code < 0 ) return code;
  370.         h -= rh;
  371.     }
  372.     rw = w % tw;
  373.     if ( rw )
  374.     {    code = gx_default_tile_rectangle(dev, tile, x + w - rw, y, rw, h, czero, cone, px, py);
  375.         if ( code < 0 ) return code;
  376.         w -= rw;
  377.     }
  378.     /* Now w and h are multiples of tw and th respectively, */
  379.     /* and greater than 1.  Start by doing one tile the slow way. */
  380.     code = gx_default_tile_rectangle(dev, tile, x, y, tw, th, czero, cone, px, py);
  381.     if ( code < 0 ) return code;
  382.     /* Now replicate the tile. */
  383.     getimage(x, y, x + tw - 1, y + th - 1, &image[0]);
  384.     for ( ty = h; (ty -= th) >= 0; )
  385.     {    for ( tx = w; (tx -= tw) >= 0; )
  386.             putimage(x + tx, y + ty, &image[0], COPY_PUT);
  387.     }
  388.     return 0;
  389. }
  390.  
  391.  
  392. /* Draw a line */
  393. int
  394. bgi_draw_line(gx_device *dev, int x0, int y0, int x1, int y1,
  395.   gx_color_index color)
  396. {    setcolor((int)color);
  397.     setlinestyle(SOLID_LINE, 0, NORM_WIDTH);  /* solid, one pixel wide */
  398.     line(x0, y0, x1, y1);
  399.     return 0;
  400. }
  401.  
  402. /* ------ Platform font procedures ------ */
  403.  
  404. /*
  405.  * Note: Stroked BGI fonts lie about their height and baseline:
  406.  * the textheight is actually the position of the baseline,
  407.  * and the only way to find the actual height is to scan the bits.
  408.  */
  409.  
  410. /* Define xfont procedures. */
  411. private xfont_proc_lookup_font(bgi_lookup_font);
  412. private xfont_proc_char_xglyph(bgi_char_xglyph);
  413. private xfont_proc_char_metrics(bgi_char_metrics);
  414. private xfont_proc_render_char(bgi_render_char);
  415. private xfont_proc_release(bgi_release);
  416. private gx_xfont_procs bgi_xfont_procs = {
  417.     bgi_lookup_font,
  418.     bgi_char_xglyph,
  419.     bgi_char_metrics,
  420.     bgi_render_char,
  421.     bgi_release
  422. };
  423.  
  424. /* Return the xfont procedure record. */
  425. gx_xfont_procs *
  426. bgi_get_xfont_procs(gx_device *dev)
  427. {    return &bgi_xfont_procs;
  428. }
  429.  
  430. /* Define a BGI xfont. */
  431. typedef struct bgi_xfont_s {
  432.     gx_xfont_common common;
  433.     const char _ds *fname;
  434.     int index;            /* BGI font index */
  435.     gs_int_point ratio;        /* scaling ratio for character */
  436.     int base_size;            /* height of 4x 'A' */
  437.     int baseline;
  438. } bgi_xfont;
  439. gs_private_st_simple(st_bgi_xfont, bgi_xfont, "bgi_xfont");
  440.  
  441. /* Different versions of Turbo C and Borland C++ have different fonts.... */
  442. private bgi_xfont all_fonts[] = {
  443.     { { &bgi_xfont_procs}, "Courier", DEFAULT_FONT },
  444. #ifdef SANSSERIF_FONT
  445.     { { &bgi_xfont_procs}, "Helvetica", SANSSERIF_FONT },
  446. #else
  447. # ifdef SANS_SERIF_FONT
  448.     { { &bgi_xfont_procs}, "Helvetica", SANS_SERIF_FONT },
  449. # endif
  450. #endif
  451. #ifdef SIMPLEX_FONT
  452.     { { &bgi_xfont_procs}, "Times-Roman", SIMPLEX_FONT },
  453. #endif
  454. #ifdef BOLD_FONT
  455.     { { &bgi_xfont_procs}, "Times-Bold", BOLD_FONT }
  456. #endif
  457. };
  458.  
  459. /* Keep a record of a character temporarily rendered to the screen. */
  460. typedef struct char_image_s {
  461.     char str[2];
  462.     gs_int_point size;
  463.     uint image_size;
  464.     char *image;
  465. } char_image;
  466.  
  467. /* Forward references */
  468. private bgi_xfont *char_set_font(P1(gx_xfont *));
  469. private int char_set_image(P2(byte, char_image *));
  470. private void char_bbox(P3(bgi_xfont *, char_image *, gs_int_rect *));
  471. private void char_restore_image(P1(char_image *));
  472.  
  473. /* Look up a font. */
  474. gx_xfont *
  475. bgi_lookup_font(gx_device *dev, const byte *fname, uint len,
  476.   int encoding_index, const gs_uid *puid, const gs_matrix *pmat,
  477.   gs_memory_t *mem)
  478. {    float px, py;
  479.     int i;
  480.     if ( pmat->xy != 0 || pmat->yx != 0 || pmat->xx <= 0 || pmat->yy >= 0 )
  481.         return NULL;    /* unsupported transformation */
  482.     px = pmat->xx * 1000;
  483.     py = pmat->yy * -1000;
  484.     for ( i = 0; i < countof(all_fonts); i++ )
  485.     {    bgi_xfont *pf = &all_fonts[i];
  486.         if ( strlen(pf->fname) == len && !memcmp(pf->fname, fname, len) )
  487.         {    long rx, ry;
  488.             bgi_xfont *spf;
  489.             settextstyle(pf->index, HORIZ_DIR, 1);
  490.             if ( pf->base_size == 0 )    /* not set yet */
  491.             {    setusercharsize(1, 1, 1, 1);
  492.                 pf->base_size = textheight("A");
  493.             }
  494.             rx = px * 64 / pf->base_size;
  495.             ry = py * 64 / pf->base_size;
  496.             if ( rx <= 0 || ry <= 0 )
  497.                 return NULL;
  498.             spf = gs_alloc_struct(mem, bgi_xfont,
  499.                     &st_bgi_xfont, "bgi_lookup_font");
  500.             if ( spf == 0 )
  501.                 return NULL;
  502.             *spf = *pf;
  503.             spf->ratio.x = rx;
  504.             spf->ratio.y = ry;
  505.             char_set_font((gx_xfont *)spf);
  506.             spf->baseline = textheight("A");  /* (see above) */
  507.             return (gx_xfont *)spf;
  508.         }
  509.     }
  510.     return NULL;        /* unsupported font name */
  511. }
  512.  
  513. /* Convert a character name or index to an xglyph code. */
  514. gx_xglyph
  515. bgi_char_xglyph(gx_xfont *xf, gs_char chr, int encoding_index,
  516.   gs_glyph glyph, gs_proc_glyph_name_t glyph_name_proc)
  517. {    if ( (encoding_index & ~1) != 0 ||  /* Standard & ISOLatin1 only */
  518.          chr < 32 || chr > 126    /* ASCII only */
  519.        )
  520.         return gx_no_xglyph;
  521.     return chr;
  522. }
  523.  
  524. /* Get the metrics for a character. */
  525. int
  526. bgi_char_metrics(gx_xfont *xf, gx_xglyph xg, int wmode,
  527.   gs_point *pwidth, gs_int_rect *pbbox)
  528. {    bgi_xfont *pf = char_set_font(xf);
  529.     char_image ci;
  530.     if ( wmode != 0 )
  531.         return gs_error_undefined;
  532.     if ( char_set_image((byte)xg, &ci) < 0 )
  533.         return gs_error_ioerror;
  534.     char_bbox(pf, &ci, pbbox);
  535.     pwidth->x = textwidth(ci.str);
  536.     pwidth->y = 0;
  537.     if ( pwidth->x == pbbox->q.x && pbbox->p.x == 0 )
  538.     {    /* This is a badly designed font with no inter-character */
  539.         /* spacing.  Add 1 pixel. */
  540.         pwidth->x++;
  541.     }
  542.     char_restore_image(&ci);
  543.     return 0;
  544. }
  545.  
  546. /* Render a character. */
  547. int
  548. bgi_render_char(gx_xfont *xf, gx_xglyph xg, gx_device *target,
  549.   int xo, int yo, gx_color_index color, int required)
  550. {    bgi_xfont *pf = char_set_font(xf);
  551.     char_image ci;
  552.     gs_int_rect bbox;
  553.     int xi, yi;
  554.     if ( target->dname == gs_bgi_device.dname )
  555.     {    /* Write directly to a BGI device. */
  556.         ci.str[0] = (char)xg;
  557.         ci.str[1] = 0;
  558.         setcolor((int)color);
  559.         outtextxy(xo, yo - pf->baseline, ci.str);
  560.         return 0;
  561.     }
  562.     if ( !required )
  563.         return gs_error_ioerror;    /* any error will do */
  564.     if ( char_set_image((byte)xg, &ci) < 0 )
  565.         return gs_error_ioerror;
  566.     char_bbox(pf, &ci, &bbox);
  567.     for ( yi = bbox.p.y; yi < bbox.q.y; yi++ )
  568.     {    for ( xi = bbox.p.x; xi < bbox.q.x; xi++ )
  569.           if ( getpixel(xi, yi + pf->baseline) != WHITE )
  570.         {    (*dev_proc(target, fill_rectangle))(target,
  571.                 xi + xo, yi + yo, 1, 1, color);
  572.         }
  573.     }
  574.     char_restore_image(&ci);
  575.     return 0;
  576. }
  577.  
  578. /* Release an xfont. */
  579. private int
  580. bgi_release(gx_xfont *xf, gs_memory_t *mem)
  581. {    if ( mem != NULL )
  582.         gs_free_object(mem, xf, "bgi_release");
  583.     return 0;
  584. }
  585.  
  586. /* ------ Font utilities ------ */
  587.  
  588. /* Set up a font. */
  589. private bgi_xfont *
  590. char_set_font(gx_xfont *xf)
  591. {    bgi_xfont *pf = (bgi_xfont *)xf;
  592.     settextstyle(pf->index, HORIZ_DIR, 0);
  593.     setusercharsize(pf->ratio.x, 64, pf->ratio.y, 64);
  594.     return pf;
  595. }
  596.  
  597. /* Write a character onto the screen. */
  598. private int
  599. char_set_image(byte ch, char_image *pci)
  600. {    char *str = pci->str;
  601.     int w, h;
  602.     uint size;
  603.     char *image;
  604.     str[0] = ch;
  605.     str[1] = 0;
  606.     w = textwidth(str);
  607.     h = textheight(str) << 1;    /* (see above) */
  608.     if ( w != 0 && h != 0 )
  609.     {    size = imagesize(0, 0, w - 1, h - 1);
  610.         image = malloc(size);
  611.         if ( image == 0 ) return -1;    /* allocation failed */
  612.         getimage(0, 0, w - 1, h - 1, image);
  613.         setfillstyle(SOLID_FILL, WHITE);
  614.         bar(0, 0, w - 1, h - 1);        /* clear */
  615.         setcolor(BLACK);
  616.         outtextxy(0, 0, str);
  617.     }
  618.     else
  619.     {    size = 0;
  620.         image = 0;
  621.     }
  622.     pci->size.x = w;
  623.     pci->size.y = h;
  624.     pci->image_size = size;
  625.     pci->image = image;
  626.     return 0;
  627. }
  628.  
  629. /* Find the bounding box of a character. */
  630. /* The character is already on the screen. */
  631. private void
  632. char_bbox(bgi_xfont *pf, char_image *pci, gs_int_rect *pbbox)
  633. {    int x0 = pci->size.x, y0 = pci->size.y, x1 = -1, y1 = 0;
  634.     int x, y;
  635.     int base = pf->baseline;
  636.     for ( y = pci->size.y; --y >= 0; )
  637.       for ( x = pci->size.x; --x >= 0; )
  638.         if ( getpixel(x, y) != WHITE )
  639.     {    if ( x < x0 ) x0 = x;
  640.         if ( x > x1 ) x1 = x;
  641.         if ( y < y0 ) y0 = y;
  642.         if ( y > y1 ) y1 = y;
  643.     }
  644.     if ( x0 > x1 )        /* blank */
  645.         pbbox->p.x = pbbox->q.x = pbbox->p.y = pbbox->q.y = 0;
  646.     else
  647.     {    pbbox->p.x = x0;
  648.         pbbox->p.y = y0 - base;
  649.         pbbox->q.x = x1 + 1;
  650.         pbbox->q.y = y1 + 1 - base;
  651.     }
  652. }
  653.  
  654. /* Restore the image under the character. */
  655. private void
  656. char_restore_image(char_image *pci)
  657. {    if ( pci->image != 0 )        /* might have been empty */
  658.     {    putimage(0, 0, pci->image, COPY_PUT);
  659.         free(pci->image);
  660.     }
  661. }
  662.